package main

import (
	"fmt"
	"sync"
	"time"
)

/**
sync.Cond（条件变量） 是基于互斥锁的，它必须有互斥锁的支撑才能发挥作用
- sync.Cond 条件变量是用来协调想要访问共享资源的那些线程，当共享资源的状态发生改变时，它可以用来通知被互斥锁阻塞的线程
- 条件变量的初始化离不开互斥锁，并且它的方法也是基于互斥锁的。
- 条件变量有三个方法，等待通知(wait),单发通知(signal),广播通知(boadcast)。当互斥锁锁定时，可以进行等待通知;当互斥锁解锁时，可以进行单发通知和广播通知。
- 条件变量的Signal方法只会唤醒一个因等待通知而被阻塞的 goroutine，而它的Broadcast方法却可以唤醒所有为此而等待的 goroutine
**/

var cond sync.Cond
var group int

func main() {
	cond = *sync.NewCond(&sync.Mutex{})

	wg := do(job1, job2, job2_1, job3)

	for group = 1; group <= 4; group++ {
		fmt.Printf("唤醒分组%v\n", group)
		//确定第一个函数执行完之后全部被唤醒
		cond.Broadcast()

		time.Sleep(time.Second)
		//确定第一个函数执行完之后唤醒某一个函数
		//cond.Signal()
	}
	wg.Wait()
}

func do(fs ...func()) *sync.WaitGroup {
	wg := &sync.WaitGroup{}

	for _, fn := range fs {
		wg.Add(1)
		go func(f func()) {
			defer wg.Done()
			f()
		}(fn)
	}

	return wg
}

func job1() {
	for group != 1 {
		cond.L.Lock()
		cond.Wait()
		cond.L.Unlock()
	}
	fmt.Println("job1")
}
func job2() {
	for group != 2 {
		cond.L.Lock()
		cond.Wait()
		cond.L.Unlock()
	}
	fmt.Println("job2")
}

func job2_1() {
	for group != 2 {
		cond.L.Lock()
		cond.Wait()
		cond.L.Unlock()
	}
	fmt.Println("job2.1")
}
func job3() {

	for group != 3 {
		cond.L.Lock()
		cond.Wait()
		cond.L.Unlock()
	}
	fmt.Println("job3")
}
